
// Basic iterators
// Type terminology:
// Set : A[elem]=1
// Dict: A[key]=[val1,val2,...]
// Tab : A[i]=elem

//-----------------------------------------------------------------------------------------------

function empty_iterator() {}
empty_iterator.prototype.end = function() { return true;}


//-----------------------------------------------------------------------------------------------

function range_iterator(rmin,rmax,rstep) {   
  this._rstep=rstep || 1;  
  if(this._rstep > 0) {
    this._last=Math.max(rmin,rmax)+1;
    this._first=Math.min(rmin,rmax);
  } else {
    this._last=Math.min(rmin,rmax)-1;
    this._first=Math.max(rmin,rmax);
  }
  this._curr=this._first;
}

range_iterator.prototype.end = function() { return this._curr==this._last;}  
range_iterator.prototype.next = function() {  this._curr+=this._rstep;}
range_iterator.prototype.getValue = function() { return this._curr;}
range_iterator.prototype.reset = function () { this._curr=this._first; }

function claimrange_iterator(range,last) {   
  this.base = new range_iterator;
  var _range=range.split("-");
  if(!range[0]) range[0]=1;       // handle "-30"
  if(!range[1]) range[1]=last;  // hanlde "30-"
  this.base(parseInt(range[0]),parseInt(range[1]));
}
claimrange_iterator.prototype=range_iterator.prototype;
claimrange_iterator.prototype.constructor=claimrange_iterator;


//-----------------------------------------------------------------------------------------------

function char_iterator(txt) {
	this._str = txt || "";
	this._i=0;
}

char_iterator.prototype.getValue = function () { return this._str.charAt(this._i);}
char_iterator.prototype.next     = function () { this._i++; }
char_iterator.prototype.end      = function () { return this._i >= this._str.length;}
char_iterator.prototype.tail     = function () { return this._str.substr(this._i);}


//------------------------------------------------------------------------------------------


function text_iterator(txt,skip,sep) {
  this._str=txt || "";
  this._i=0;
  this._j=0;
  this._skip=skip  || " \t\n";             // separators, ignore 
  this._sep= (sep  || ";.,-")+this._skip;  // separators, return 
 
  this._next = function () { while(this._i< this._str.length && this._skip.indexOf(this._str.charAt(this._i))>=0) {this._i++;};
                             this._j=this._i;
                             while(this._j< this._str.length && this._sep.indexOf(this._str.charAt(this._j))<0) {this._j++;};
                       
                             return (this._j==this._i)? this._j+1 : this._j;
  }
  this._j=this._next();
}
text_iterator.prototype.next = function() { this._i=this._j; this._j=this._next();}
text_iterator.prototype.getValue  = function () { return this._str.substring(this._i, this._j); }
text_iterator.prototype.end = function () { return this._i >= this._str.length;}
text_iterator.prototype.tail = function () { return this._str.substr(this._i); }


//------------------------------------------------------------------------------------------

// from f to l (excluded)
function array_iterator(tab,f,l) {
  //alert("array_iterator "+(f||"nof")+ " "+(l||"nol"));
  if(tab && tab.length>0) {
    this._first=(f || 0);
    this._tab = tab;
    this._end = this._tab.length;
    if(l && this._end>l) this._end=l+1;
  } else {
    this._tab = [];
    this._first=0;
    this._end= 0;
  }
  this._idx=this._first;
}

array_iterator.prototype.getElem  = function() { return this._tab[this._idx];}
array_iterator.prototype.end =  function () { return this._idx >= this._end;}
array_iterator.prototype.next = function() { this._idx++;}
array_iterator.prototype.count = function() { return this._tab.length - this._idx;}
array_iterator.prototype.reset = function() {  this._idx=0; return this}
array_iterator.prototype.length = function() { return this._tab.length;}
array_iterator.prototype.first = function() { return this._tab[this._first];}
array_iterator.prototype.last = function() { return this._tab[this._end-1];} 
array_iterator.prototype.getIndex = function() { return this._idx;}
array_iterator.prototype.iterator = function() { return new this.constructor(this._tab);}
array_iterator.prototype.getValue = function() { return this.getElem();}

array_iterator.prototype.reduce   = function() { 
  var set1=[];
  for(var iter=this.iterator();!iter.end(); iter.next()){
    set1[iter.getValue()]=1;
  }
  var tab=[];
  for(var e in set1) {
    tab.push(e);
  }
  return new this.constructor(tab);
}



//------------------------------------------------------------------------------------------


// array of arrays tab[key]= vals[]
function dict_iterator(dict) {
  this.base= array_iterator;
  this.base(dict);
}
dict_iterator.prototype= new array_iterator;
dict_iterator.prototype.constructor=dict_iterator;

// returns tab
dict_iterator.prototype.getKeys   = function(val) {
  var tab=[];
  if(val) {
    for(var kiter in this._tab){
      for(var viter=new array_iterator(kiter.getValue());!viter.end();viter.next()){
	if(viter.getValue()==val){
	  tab.push(kiter);
	}
      }
    }
  } else {
    for(var kiter in this._tab){
      tab.push(kiter);
    }
  }  
  return tab;
} 

// returns tab 
dict_iterator.prototype.getValues   = function(key) {
  if(key) {
    return this._tab[key];
  } else {
    var tab=[];
    for(var kiter in this._tab){
      for(var viter=new array_iterator(kiter.getValue());!viter.end();viter.next()){
	tab.push(viter.getValue());
      }
    }        
  }
}


//-----------------------------------------------------------------------------------------------

function pair(f,s) {
  this.first;
  this.second;

  if(f) {
    this.first=f;
  }
  if(s) {
    this.second=s;
  }
}

//------------------------------------------------------------------------------------------

function firstpair_iterator(tab){
  this.base= array_iterator;
  this.base(tab);
}

firstpair_iterator.prototype= new array_iterator;
firstpair_iterator.prototype.constructor=firstpair_iterator;
firstpair_iterator.prototype.getValue= function () { return this.getElem().first;};
firstpair_iterator.prototype.reduce   = function() { 
  var tab1=[];
  for(var iter=this.iterator();!iter.end(); iter.next()){
    if(!tab1[iter.getElem().first]) {
      tab1[iter.getElem().first]=[];
    }
    tab1[iter.getElem().first].push(iter.getElem().second);
  }
  return new dict_iterator(tab1);
}

//------------------------------------------------------------------------------------------

function secondpair_iterator(tab){
  this.base= array_iterator;
  this.base(tab);
}
secondpair_iterator.prototype= new array_iterator;
secondpair_iterator.prototype.constructor=secondpair_iterator;
secondpair_iterator.prototype.getValue= function () { return this.getElem().second;};


//------------------------------------------------------------------------------------------

// put elements from iterator into a Set if cond

function iter_toSet(iter,cond,s) {
  var tmpset=s || [];
  if(cond) {
    while(!iter.end()) {
      if(cond(iter.getValue())){
	tmpset[iter.getValue()]=1;
      }
      iter.next();
    }
  } else {
    while(!iter.end()) {
      tmpset[iter.getValue()]=1;      
      iter.next();
    }
  }
  return tmpset;
}

//-----------------------------------------------------------------------------------------------


function iter_mapreduce(iter,mapf,reducef) {
  var out;
  while(!iter.end()) {
    out=reducef(out,mapf(iter.getValue()));
    iter.next();
  }
  return out;
}
 


